﻿#region Copyright ©2008-2011, Technische Universitaet Muenchen
// ====================================================================================================
//
// Last Changed by       $Author: then $
// Last Changed Date     $LastChangedDate: 2009-05-16 14:58:56 +0200 (Sat, 16 May 2009) $
// Last Changed Revision $Rev: 144 $
//
// ====================================================================================================
#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using Tum.CollabXT.TFS.Resources;
using System.Globalization;


namespace Tum.CollabXT.TFS
{
    public class TFSToolProvider : IToolProvider
    {
        /// <summary>
        /// Gets the belonging process provider.
        /// </summary>
        public IProcessProvider ProcessProvider
        {
            get;
            private set;
        }


        /// <summary>
        /// Gets the log used with this provider.
        /// </summary>
        public Log ProcessLog
        {
            get;
            private set;
        }

        /// <summary>
        /// Output path for the final process template.
        /// </summary>
        public string OutputPath
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the template path.
        /// </summary>
        public string TemplatePath
        {
            get
            {
                return _TemplatePath;
            }
            set
            {
                if(value != null)
                    PrepareProcessTemplate(value);
                _TemplatePath = value;
            }
        }
        private string _TemplatePath;

        /// <summary>
        /// Gets the name of the template. It is set when the template's path is specified.
        /// </summary>
        public string TemplateName
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the intended TFS version for the template. It is set when the template's path is specified.
        /// </summary>
        public TfsVersion TemplateVersion
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the intended TFS language for the template. It is set when the template's path is specified.
        /// </summary>
        public CultureInfo TemplateLanguage
        {
            get;
            private set;
        }

        /// <summary>
        /// Configuration document of the template.
        /// </summary>
        private XmlDocument TemplateConfig;

        /// <summary>
        /// TemplateProcessor instance used.
        /// </summary>
        private TemplateProcessor TplProcessor;

        /// <summary>
        /// Gets or sets the path of the process guidance source files.
        /// </summary>
        public string ProcessGuidancePath
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the default page of the process guidance.
        /// See also ProcessGuidancePath.
        /// </summary>
        public string ProcessGuidanceDefaultPage
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the list of document libraries that are included in the template.
        /// Key of each entry specifies the library name, value sets the source folder.
        /// </summary>
        public List<KeyValuePair<string, string>> DocumentLibraries
        {
            get;
            private set;
        }



        #region IToolProvider Members
        public static string GetProviderName()
        {
            return Language.ProviderInfo_Name;
        }

        public static string GetProviderDescription()
        {
            return Language.ProviderInfo_Description;
        }

        public static string GetProviderAuthor()
        {
            return "© 2011 TU München";
        }


        public Dictionary<string, object> ExtendedData
        {
            get
            {
                throw new NotSupportedException();
            }
        }

        public TFSToolProvider()
        {
            DocumentLibraries = new List<KeyValuePair<string, string>>();

            //Initialize template processor.
            TplProcessor = new TemplateProcessor(this);
        }


        public void Initialize(IProcessProvider processProvider, Log log)
        {
            this.ProcessProvider = processProvider;
            this.ProcessLog = log;
        }

        public void Process()
        {
            #region Preparations
            if (ProcessProvider == null)
                throw new TFSException("ProcessProvider has not been set. Call Initialize before.");

            if (!OutputPath.EndsWith("/", StringComparison.OrdinalIgnoreCase) && !OutputPath.EndsWith("\\", StringComparison.OrdinalIgnoreCase))
                OutputPath += "/";

            if (!TemplatePath.EndsWith("/", StringComparison.OrdinalIgnoreCase) && !TemplatePath.EndsWith("\\", StringComparison.OrdinalIgnoreCase))
                TemplatePath += "/";
            #endregion

            ProcessLog.AddEntry(Language.Processing_PluginInfo);
            ProcessLog.AddEntry(Language.Processing_Copyright);
            ProcessLog.AddSeparator();

            //Process
            TplProcessor.Process(TemplateConfig);
        }

        public string GetConfigPageName()
        {
            return "TFSConfigFrame.xaml";
        }

        public void Serialize(XmlNode outputParentNode)
        {
            XmlDocument xmlDoc = outputParentNode.OwnerDocument;

            //General settings
            XmlNode settingsNode = xmlDoc.CreateElement("settings");
            outputParentNode.AppendChild(settingsNode);

            XmlAttribute outputPathAttribute = xmlDoc.CreateAttribute("outputPath");
            outputPathAttribute.Value = OutputPath;
            settingsNode.Attributes.Append(outputPathAttribute);

            XmlAttribute templatePathAttribute = xmlDoc.CreateAttribute("templatePath");
            templatePathAttribute.Value = TemplatePath;
            settingsNode.Attributes.Append(templatePathAttribute);

            //Process guidance
            XmlAttribute guidancePathAttribute = xmlDoc.CreateAttribute("wssDumpPath");
            guidancePathAttribute.Value = ProcessGuidancePath;
            settingsNode.Attributes.Append(guidancePathAttribute);

            XmlAttribute guidanceDefaultPageAttribute = xmlDoc.CreateAttribute("guidanceDefaultPage");
            guidanceDefaultPageAttribute.Value = ProcessGuidanceDefaultPage;
            settingsNode.Attributes.Append(guidanceDefaultPageAttribute);

            //Document libraries
            XmlNode librariesNode = xmlDoc.CreateElement("libraries");
            outputParentNode.AppendChild(librariesNode);

            foreach (var curLib in DocumentLibraries)
            {
                XmlNode libNode = xmlDoc.CreateElement("lib");
                librariesNode.AppendChild(libNode);

                XmlAttribute libNameAttribute = xmlDoc.CreateAttribute("name");
                libNameAttribute.Value = curLib.Key;
                libNode.Attributes.Append(libNameAttribute);

                XmlAttribute libPathAttribute = xmlDoc.CreateAttribute("path");
                libPathAttribute.Value = curLib.Value;
                libNode.Attributes.Append(libPathAttribute);
            }
        }

        public void Deserialize(XmlNode inputParentNode)
        {
            //General settings
            XmlNode settingsNode = inputParentNode.SelectSingleNode("settings");
            if (settingsNode == null)
                return;

            OutputPath = null;
            XmlNode outputPathNode = settingsNode.Attributes["outputPath"];
            if(outputPathNode != null)
                OutputPath = outputPathNode.Value;

            TemplatePath = null;
            XmlNode templatePathNode = settingsNode.Attributes["templatePath"];
            if(templatePathNode != null)
            {
                TemplatePath = templatePathNode.Value;
                if (!Directory.Exists(TemplatePath))
                    TemplatePath = null;
            }

            //Process guidance
            ProcessGuidancePath = null;
            XmlNode guidancePathAttribute = settingsNode.Attributes["wssDumpPath"];
            if (guidancePathAttribute != null)
            {
                ProcessGuidancePath = guidancePathAttribute.Value;
                if (!Directory.Exists(ProcessGuidancePath))
                    ProcessGuidancePath = null;
            }

            var guidanceDefaultPageAttribute = settingsNode.Attributes["guidanceDefaultPage"];
            if (guidanceDefaultPageAttribute != null)
            {
                ProcessGuidanceDefaultPage = guidanceDefaultPageAttribute.Value;
            }

            //Document libraries
            XmlNode librariesNode = inputParentNode.SelectSingleNode("libraries");
            if (librariesNode != null)
            {
                foreach (XmlNode libNode in librariesNode.SelectNodes("lib"))
                {
                    XmlAttribute libNameAttribute = libNode.Attributes["name"];
                    XmlAttribute libPathAttribute = libNode.Attributes["path"];
                    if(libNameAttribute != null && libPathAttribute != null)
                    {
                        if (!string.IsNullOrEmpty(libNameAttribute.Value) && !string.IsNullOrEmpty(libPathAttribute.Value))
                            DocumentLibraries.Add(new KeyValuePair<string, string>(libNameAttribute.Value, libPathAttribute.Value));
                    }
                }
            }
        }

        #endregion

        /// <summary>
        /// Loads the TemplateConfig.xml for a specified template and checks its contents for validity.
        /// Sets the properties TemplateName and TemplateVersion.
        /// </summary>
        /// <exception cref="IOException"></exception>
        /// <exception cref="TemplateConfigInvalidException"></exception>
        /// <param name="value"></param>
        private void PrepareProcessTemplate(string processTemplatePath)
        {
            if (processTemplatePath == null)
                throw new ArgumentNullException("processTemplatePath");

            //Check template config file existance and load it
            string templateConfigPath = processTemplatePath + "/TemplateConfig.xml";

            TemplateConfig = new XmlDocument();
            try
            {
                TemplateConfig.Load(templateConfigPath);
            }
            catch (XmlException)
            {
                throw new TemplateConfigInvalidException();
            }

            XmlNode configRootNode = TemplateConfig.SelectSingleNode("TfsTemplateConfig");
            if (configRootNode == null)
                throw new TemplateConfigInvalidException();

            //Get template name
            XmlNode nameNode = configRootNode.SelectSingleNode("Name");
            if (nameNode != null)
                TemplateName = nameNode.InnerText;

            //Get intended TFS version
            XmlNode versionNode = configRootNode.SelectSingleNode("TfsVersion");
            if (versionNode == null)
                throw new TemplateConfigInvalidException("TfsVersion missing");
            switch(versionNode.InnerText)
            {
                case "2005":
                case "2008":
                    TemplateVersion = TfsVersion.Tfs2005;
                    break;

                case "2010":
                    TemplateVersion = TfsVersion.Tfs2010;
                    break;

                default:
                    throw new TemplateConfigInvalidException("TfsVersion invalid");
            }

            //Get intended TFS language
            XmlNode languageNode = configRootNode.SelectSingleNode("TfsLanguage");
            if (languageNode == null)
                throw new TemplateConfigInvalidException("TfsLanguage missing");
            try
            {
                TemplateLanguage = CultureInfo.GetCultureInfo(languageNode.InnerText);
            }
            catch (ArgumentException)
            {
                throw new TemplateConfigInvalidException("TfsLanguage invalid");
            }


            //Get language resource file location
            XmlNode langFileNode = configRootNode.SelectSingleNode("TextResourcesFile");
            if(langFileNode == null)
                throw new TemplateConfigInvalidException("TextResourcesFile missing");
            TplProcessor.LanguageFileName = langFileNode.InnerText;

            //Get inner template folder
            XmlNode innerTplFolderNode = configRootNode.SelectSingleNode("TemplateFolder");
            if(innerTplFolderNode == null)
                throw new TemplateConfigInvalidException("TemplateFolder missing");
            TplProcessor.InnerTemplateFolder = innerTplFolderNode.InnerText;

            //Check tasks node existance
            XmlNode tasksNode = configRootNode.SelectSingleNode("Tasks");
            if (tasksNode == null)
                throw new TemplateConfigInvalidException("Tasks node missing");

            //Check 'workitem' node existance (exactly one must exist)
            if (tasksNode.SelectNodes("Task[@type='workitemtracking']").Count != 1)
                throw new TemplateConfigInvalidException("Exactly one workitemtracking Task must exist");
        }

        public static string GetTfsVersionString(TfsVersion version)
        {
            switch(version)
            {
                case TfsVersion.Tfs2005:
                    return Language.Version_TFS_2005;
                case TfsVersion.Tfs2010:
                    return Language.Version_TFS_2010;
                default:
                    throw new TFSException("Unknown TFS version");
            }
        }
    }

    /// <summary>
    /// Enumeration for versions of TFS.
    /// </summary>
    public enum TfsVersion
    {
        /// <summary>
        /// TFS 2005 or TFS 2008.
        /// </summary>
        Tfs2005,

        /// <summary>
        /// TFS 2010.
        /// </summary>
        Tfs2010
    }
}